package org.java.document.uploader.Controller;

import org.apache.commons.io.IOUtils;
import org.java.commons.vo.Result;
import org.java.document.uploader.domain.DocumentFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;


@RestController
@RequestMapping("/upload")
public class UploadController {

    @Autowired
//    连接池自动管理，提供了一个高度封装的“RedisTemplate”类
    // Spring Boot在加入Redis相关依赖以后，已经自动配置了RedisTemplate<String, String>的操作模板。
    // <String, String>前者表示key的类型，后者是value的类型。
    private RedisTemplate<String, DocumentFile> documentFileRedisTemplate;
//上传文档
    @PostMapping
    public Result upload(@RequestParam("file") MultipartFile file) throws IOException {
//        解压缩文件getOriginalFilename得到原始文件名
        try(InputStream in = file.getInputStream();
            ZipInputStream zipInputStream = new ZipInputStream(in)
        ){
            unzipFile(file.getOriginalFilename(),zipInputStream);
            // 发布一个消息到队列中，所有在线的消费者都可以收到。
            // document-uploader可以随意，表示通道的名称。火车的车次。
            // fileName就是通知的内容，这里是直接把字符串（原始文件名）作为内容。
            // 消费者收到消息以后，就通过此名字获取List里面的信息（很多的文件）。
            this.documentFileRedisTemplate.convertAndSend("document-uploader", file.getOriginalFilename());
        }
        return Result.ok("文档上传成功，并且已经放入队列中!");
    }

    private void unzipFile(String filename, ZipInputStream zipInputStream) {
        try {
//            循环防止entry可能是空
            while (true) {
//        取得zip文件
                ZipEntry entry = zipInputStream.getNextEntry();
//                如果entry是空，就什么都不用干，直接结束,进入下一个
                if (entry==null){
                    break;
                }
                //        isDirectory目录   如果是文件夹，重新再来一遍,否则就是文件内容了，读取到内容，就放到Redis里面
                if (entry.isDirectory()) {
                    this.unzipFile(filename, zipInputStream);
                } else {
//          读取到文件的内容,并且放入Redis里面
                readAndSaveFile(filename,entry,zipInputStream);
                }
//                关闭处理下一个文件
                zipInputStream.closeEntry();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//读取并保存文件
    private void readAndSaveFile(String filename, ZipEntry entry, ZipInputStream zipInputStream) throws IOException {
//        解压缩文件内容
//        读取内容
        ByteArrayOutputStream out = new ByteArrayOutputStream();
//        拷贝zipInputStream的东西到our里面
        IOUtils.copy(zipInputStream,out);
//        读取文件  toByteArray就是就一个字节流转换为一个 byte数组
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
//      按行读取全部内容
//       把内容全部转换为一个字符串
        List<String> contents = IOUtils.readLines(in,"utf-8");
//        使用StringBuilder拼接
        StringBuilder builder = new StringBuilder();
        contents.forEach(builder::append);


//        创建文件夹,放入文件
        DocumentFile df = new DocumentFile();
//        使用原来的名字
        df.setName(entry.getName());
//        转换成字符串
        df.setContent(builder.toString());
//      把内容放进documentFileRedisTemplate
//      boundListOps绑定一个List操作，key的名称是上传的原始文件名
//        leftPush把文件从左插入 ；在指定的第一个值出现的左边添加值
        // 添加一个元素到List里面
        // leftPush表示把新解压的文件，从List的左边放进去
        // Redis里面的List就是一个双向队列，但是它不具备通知的能力，类似于Java的LinkedList。
        this.documentFileRedisTemplate.boundListOps(filename).leftPush(df);
    }

}
